package org.wisdom.data.jpa.support;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.mapping.*;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.wisdom.data.jpa.annotation.SerialNumber;
import org.wisdom.data.jpa.entity.WisdomEntity;
import org.wisdom.data.jpa.support.service.CodeService;

import java.lang.annotation.Annotation;
import java.util.Optional;
import java.util.function.Predicate;

/**
 * 编码处理类
 *
 * @author 张智研
 * @date 2022/05/17 17:38
 **/
@Component
@Slf4j
public class WisdomEntityHandler implements InitializingBean {

    private static final AccessOptions.SetOptions OPTIONS = AccessOptions.defaultSetOptions().skipNulls()
        .withCollectionAndMapPropagation(AccessOptions.SetOptions.Propagation.SKIP);
    private static final Predicate<? super PersistentProperty<?>> HAS_COLLECTION_PROPERTY =
        it -> it.isCollectionLike() || it.isMap();
    private final PersistentEntities entities;
    private final MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext;
    private final CodeService codeService;

    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    public WisdomEntityHandler(
        MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext,
        CodeService codeService) {
        this.mappingContext = mappingContext;
        this.entities = PersistentEntities.of(mappingContext);
        this.codeService = codeService;
    }

    /**
     * 获取注解断言
     *
     * @author 张智研
     * @date 2022/05/18 14:14
     * @return java.util.function.Predicate<org.springframework.data.mapping.PersistentProperty<?>>
     */
    private static Predicate<PersistentProperty<?>> withAnnotation() {
        return t -> t.findAnnotation((Class<? extends Annotation>)SerialNumber.class) != null;
    }

    @Override
    public void afterPropertiesSet() throws Exception { /*  document why this method is empty */ }

    public <T> T markCreated(T source) {
        Assert.notNull(source, "Entity must not be null!");
        Optional<Object> a = Optional.of(source).flatMap(it -> {
            PersistentPropertyPaths<?, ? extends PersistentProperty<?>> serialNumberPaths =
                findPropertyPaths(it.getClass(), mappingContext);
            return this.entities.mapOnContext(it.getClass(), (context, entity) -> {
                PersistentPropertyPathAccessor<T> accessor = entity.getPropertyPathAccessor(it);
                serialNumberPaths.forEach(serialNumberPath -> {
                    SerialNumber serialNumber =
                        serialNumberPath.getRequiredLeafProperty().findAnnotation(SerialNumber.class);
                    if (serialNumber != null) {
                        String value = this.codeService.generatorCode(source, serialNumber);
                        accessor.setProperty(serialNumberPath, value, OPTIONS);
                    }
                });
                return entity;
            });
        });
        if(!a.isPresent()) {
            log.error("实体持久化实现类丢失！！！");
        }
        if(source instanceof WisdomEntity) {
            ((WisdomEntity)source).setDelFlag(false);
        }
        return source;
    }

    /**
     *
     * @author 张智研
     * @date 2022/05/18 14:15
     * @param type
     *            实体类
     * @param context
     *            上下文
     * @return org.springframework.data.mapping.PersistentPropertyPaths<?,? extends
     *         org.springframework.data.mapping.PersistentProperty<?>>
     */
    private PersistentPropertyPaths<?, ? extends PersistentProperty<?>> findPropertyPaths(Class<?> type,
        MappingContext<?, ? extends PersistentProperty<?>> context) {
        return context.findPersistentPropertyPaths(type, withAnnotation())
            .dropPathIfSegmentMatches(HAS_COLLECTION_PROPERTY);
    }
}
